from PySide6.QtCore import QAbstractTableModel, QModelIndex, Qt

"""
https://doc.qt.io/qtforpython-6/PySide6/QtCore/QAbstractTableModel.html#PySide6.QtCore.QAbstractTableModel

When subclassing QAbstractTableModel , you must implement rowCount() , columnCount() , and data()
"""


class TableModel(QAbstractTableModel):

    def __init__(self, addresses=None, parent=None):
        super().__init__(parent)

        if addresses is None:
            self.addresses = []
        else:
            self.addresses = addresses

    def rowCount(self, parent=QModelIndex()):
        """ Returns the number of rows the model holds. """
        """ https://doc.qt.io/qtforpython-6/PySide6/QtCore/QAbstractItemModel.html#PySide6.QtCore.QAbstractItemModel.rowCount """
        """ Returns the number of rows under the given parent. When the parent is valid it means that rowCount is returning the number of children of parent."""
        return len(self.addresses)

    def columnCount(self, parent=QModelIndex()):
        """ Returns the number of columns the model holds. """
        """ https://doc.qt.io/qtforpython-6/PySide6/QtCore/QAbstractItemModel.html#PySide6.QtCore.QAbstractItemModel.columnCount """
        """ Returns the number of columns for the children of the given parent.In most subclasses, the number of columns is independent of the parent. """
        return 2

    def data(self, index, role=Qt.ItemDataRole.DisplayRole):
        """ Depending on the index and role given, return data. If not
            returning data, return None (PySide equivalent of QT's
            "invalid QVariant").
        """
        """ https://doc.qt.io/qtforpython-6/PySide6/QtCore/QAbstractItemModel.html#PySide6.QtCore.QAbstractItemModel.data """
        """ Returns the (cell) data stored under the given role for the item referred to by the index. """
        if not index.isValid():
            return None

        if not (0 <= index.row() < len(self.addresses)):
            return None

        if role == Qt.ItemDataRole.DisplayRole:
            name = self.addresses[index.row()]["name"]
            address = self.addresses[index.row()]["address"]

            if index.column() == 0:
                return name
            elif index.column() == 1:
                return address

        return None

    def setData(self, index, value, role=Qt.ItemDataRole.EditRole):
        """ Adjust the data (set it to <value>) depending on the given index and role. """

        """ https://doc.qt.io/qtforpython-6/PySide6/QtCore/QAbstractItemModel.html#PySide6.QtCore.QAbstractItemModel.setData """
        """ Sets the role (cell) data for the item at index to value. Returns true if successful; otherwise returns false. """
        """ 将 view 的输入保存到关联的 model """
        if role != Qt.ItemDataRole.EditRole:
            return False

        if index.isValid() and 0 <= index.row() < len(self.addresses):
            address = self.addresses[index.row()]
            if index.column() == 0:
                address["name"] = value
            elif index.column() == 1:
                address["address"] = value

            self.dataChanged.emit(index, index, 0)  # The dataChanged() signal should be emitted if the data was successfully set.
            return True

        return False

    def headerData(self, section, orientation, role=Qt.ItemDataRole.DisplayRole):
        """ Set the headers to be displayed. """
        """ 
        https://doc.qt.io/qtforpython-6/PySide6/QtCore/QAbstractItemModel.html#PySide6.QtCore.QAbstractItemModel.headerData 
        Returns the data for the given role and section in the header with the specified orientation.
        For horizontal headers, the section number corresponds to the column number. Similarly, for vertical headers, the section number corresponds to the row number.
        """
        """" 行列表头标题 """
        if role != Qt.ItemDataRole.DisplayRole:
            return None

        if orientation == Qt.Orientation.Horizontal:  # 水平表头显示列名
            if section == 0:
                return "Name"
            elif section == 1:
                return "Address"
        elif orientation == Qt.Orientation.Vertical:  # 垂直表头显示行号
            return str(section + 1)  # 垂直表头显示行号（从1开始）
        return None

    def insertRows(self, row, count=1, parent=QModelIndex()):
        """ Insert a row into the model. 保持 model 与 view 的一致性 """
        """ https://doc.qt.io/qtforpython-6/PySide6/QtCore/QAbstractItemModel.html#PySide6.QtCore.QAbstractItemModel.insertRows """
        """ On models that support this, inserts count rows into the model before the given row. Items in the new row will be children of the item represented by the parent model index. """
        """ An insertRows() implementation must call beginInsertRows() before inserting new rows into the data structure, and it must call endInsertRows() immediately afterwards. """

        """ https://doc.qt.io/qtforpython-6/PySide6/QtCore/QAbstractItemModel.html#PySide6.QtCore.QAbstractItemModel.beginInsertRows """
        """ The parent index corresponds to the parent into which the new rows are inserted; first and last are the row numbers that the new rows will have after they have been inserted. """
        self.beginInsertRows(QModelIndex(), row, row + count - 1)  # 修改view，这是 Qt框架中定义的 自定义model必须遵循的约定

        for i in range(count):
            self.addresses.insert(row + i, {"name": "", "address": ""})  # 修改model

        """ https://doc.qt.io/qtforpython-6/PySide6/QtCore/QAbstractItemModel.html#PySide6.QtCore.QAbstractItemModel.endInsertRows """
        """ Ends a row insertion operation.When reimplementing insertRows() in a subclass, you must call this function after inserting data into the model’s underlying data store. """
        self.endInsertRows() # 修改view，这是 Qt框架中定义的 自定义model必须遵循的约定

        return True

    def removeRows(self, row, count=1, parent=QModelIndex()):
        """ Remove a row from the model. """
        """ https://doc.qt.io/qtforpython-6/PySide6/QtCore/QAbstractItemModel.html#PySide6.QtCore.QAbstractItemModel.removeRows """
        """ beginRemoveRows， endRemoveRows 同样遵循Qt框架的约定 """
        self.beginRemoveRows(QModelIndex(), row, row + count - 1)  # beginRemoveRows实际删除的数量是 last - first + 1，即包括last位置的元素

        del self.addresses[row:row + count]  # list 切片，a[i:j] 是选择索引为 k 使得 i <= k < j 的所有条目，endIndex - startIndex

        self.endRemoveRows()

        return True

    def flags(self, index):
        """ Set the item flags at the given index. Seems like we're
            implementing this function just to see how it's done, as we
            manually adjust each tableView to have NoEditTriggers.
        """
        """ https://doc.qt.io/qtforpython-6/PySide6/QtWidgets/QAbstractItemView.html#PySide6.QtWidgets.QAbstractItemView.EditTrigger """
        """ https://doc.qt.io/qtforpython-6/PySide6/QtWidgets/QAbstractItemView.html#PySide6.QtWidgets.QAbstractItemView.setEditTriggers """

        """ https://doc.qt.io/qtforpython-6/PySide6/QtCore/QAbstractItemModel.html#PySide6.QtCore.QAbstractItemModel.flags """
        """ Returns the item flags for the index. The base class implementation returns a combination of flags that enables the item (ItemIsEnabled) and allows it to be selected (ItemIsSelectable)."""
        if not index.isValid():
            return Qt.ItemFlag.ItemIsEnabled
        return Qt.ItemFlag(QAbstractTableModel.flags(self, index) | Qt.ItemFlag.ItemIsEditable)

    def set_addresses(self, addresses):
        """Replace all addresses with new data."""
        self.beginResetModel()
        self.addresses = addresses if addresses is not None else []
        self.endResetModel()